home *** CD-ROM | disk | FTP | other *** search
- /*
- File: FractalEngine.c
-
- Used to build: “Fractal 4”
-
- Written by: Jim Cathey July 1985
- Eric Traut November 1994
-
- Description:
- The following code implements a “Fractal Contour” generating
- program. See comments in the file “FractalMain.c” for more
- information.
-
- The code in this file implements the fractal-generating
- and plotting portion of the program.
- */
-
-
- #include <Quickdraw.h>
- #include <ToolUtils.h>
-
- #include <stdlib.h>
-
- #include "Fractal.h"
-
- /* Functions defined within this file */
- double RandomDouble(void);
- void CalcXs(short len, short incr, short sk);
- void CalcYs(short len, short incr, short sk);
- void CalcDiagonals(short len, short incr, short sk);
- short GetZValue(short x, short y);
- void SetZValue(short d, short x, short y);
- void SeaLevel(short *newx, short *newy, short *newz);
- short ScaleValue(short base, short numer, short denom);
- void DoPlot(short xindex, short yindex);
- void PlotTo(short x, short y, short z);
- void Rotate(short *x, short *y);
- void TiltDown(short *x, short *z);
- void DrawLine(short x, short y);
- void Cordic(short *x, short *y, short scale, short count);
- void ScaledMoveTo(short x, short y);
- void ScaledLineTo(short x, short y);
- void SetLineColor(short colorCode);
- void MoveToOffscreen(short x, short y);
- void LineToOffscreen(short x, short y);
- void LineXOffscreen(short startX, short startY, short endX, short endY);
- void LineYOffscreen(short startX, short startY, short endX, short endY);
-
- /* Global variables */
- short gMaxX, gMaxY;
- short gCurrentX, gCurrentY;
- short gPixelColor; /* Pixel value for offscreen line drawing */
- Boolean gAtLineStart; /* True at the first of the line */
- Boolean gOnLand; /* True when plotting land, else false */
- char* gPixelBase; /* Base of pixel store */
-
-
- /*
- RandomDouble
-
- This function returns a random number between -1 and 1.
- */
- double RandomDouble(void)
- {
- return (double)rand() / RAND_MAX;
- }
-
-
- /*
- CalcSurface
-
- This is the main surface-generating routine. It generates a random
- fractal surface by filling in the gPointArray array.
- */
- void CalcSurface(short level)
- {
- short i, j, length, incrby, sk;
- float power;
- CursHandle waitCursor;
- long startTicks;
-
- waitCursor = GetCursor(watchCursor);
- if (waitCursor)
- SetCursor(*waitCursor); /* Show busy cursor */
-
- gTotalFractals++;
- gFractalChanged = true;
- startTicks = TickCount();
-
- gMaxX = 1 << level;
- gMaxY = gMaxX / 2;
- for (i = 0; i <= gMaxX; i++) /* Clear the Array. Use i & incrby as temps */
- for (incrby = 0; incrby <= gMaxY; incrby++)
- (*gPointArray)[i][incrby] = 0;
-
- for (i = 1; i <= level; i++) {
- for (power = 1.0, j = 0; j < i; j++)
- power *= 1.8;
- length = 10000 / power; /* = 10000/(1.8^i) */
- incrby = gMaxX / (1 << i); /* # of line segments in a side of the triangle */
- sk = incrby * 2;
- CalcXs(length, incrby, sk);
- CalcYs(length, incrby, sk);
- CalcDiagonals(length, incrby, sk);
- }
-
- gTotalTickCount += TickCount() - startTicks;
- InitCursor(); /* Put back the arrow */
- }
-
-
- /*
- CalcXs
-
- This function calculates the fractal values in the X direction.
- It is called for each level and subdivides the existing edges
- of the fractal to get the next level.
- */
- void CalcXs(short len, short incr, short sk)
- {
- short y, x;
- short d1, d2;
-
- for (y=0; y < gMaxX; y += sk) {
- for (x = incr+y; x <= gMaxX; x += sk) {
- d1 = GetZValue(x-incr, y);
- d2 = GetZValue(x+incr, y);
- SetZValue(((d1 + d2) >> 1) + (short)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
- }
- }
- }
-
-
- /*
- CalcYs
-
- This function calculates the fractal values in the Y direction.
- It is called for each level and subdivides the existing edges
- of the fractal to get the next level.
- */
- void CalcYs(short len, short incr, short sk)
- {
- short y, x;
- short d1, d2;
-
- for (x = gMaxX; x >= 1; x -= sk)
- for (y = incr; y <= x; y += sk) {
- d1 = GetZValue(x, y + incr);
- d2 = GetZValue(x, y - incr);
- SetZValue(((d1 + d2) >> 1) + (short)(RandomDouble() * (len >> 1)) - (len >> 2), x, y);
- }
- }
-
-
- /*
- CalcDiagonals
-
- This function calculates the fractal values in the diagonal direction.
- It is called for each level and subdivides the existing edges
- of the fractal to get the next level.
- */
- void CalcDiagonals(short len, short incr, short sk)
- {
- short y, x;
- short d1, d2;
-
- for (x = 0; x < gMaxX; x += sk)
- for (y = incr; y <= gMaxX - x; y += sk) {
- d1 = GetZValue(x + y - incr, y - incr);
- d2 = GetZValue(x + y + incr, y + incr);
- SetZValue(((d1 + d2) >> 1) + (short)(RandomDouble() * (len >> 1)) - (len >> 2), x + y, y);
- }
- }
-
-
- /*
- GetZValue
-
- This function returns the Z value (i.e. the value stored in the
- two-dimensional gPlotArray array. Because the fractal is triangular
- and the array is square, we will store half the triange in the lower
- part of the array the the other half in the upper part of the
- array.
- */
- short GetZValue(short x, short y)
- {
- if (y <= gMaxY)
- return (*gPointArray)[x][y];
- else
- return (*gPointArray)[gMaxX - x][gMaxX + 1 - y];
- }
-
-
- /*
- SetZValue
-
- This function saves the Z value (i.e. the value in the
- two-dimensional gPlotArray array. Because the fractal is triangular
- and the array is square, we will store half the triange in the lower
- part of the array the the other half in the upper part of the
- array.
- */
- void SetZValue(short d, short x, short y)
- {
- if (y <= gMaxY)
- (*gPointArray)[x][y] = d;
- else
- (*gPointArray)[gMaxX - x][gMaxX + 1 - y] = d;
- }
-
-
- /*
- PlotData
-
- Our window is already open at this point, and it is cleared.
- All we have to do now is fill it. This function draws the
- 2D projection of the triangular database on the screen.
- */
- void PlotData(void)
- {
- short xindex, yindex;
-
- gOnLand = true; /* On land to start */
-
- for (xindex = 0; xindex <= gMaxX; xindex++) { /* Plot along X axis */
- gAtLineStart = true;
- for (yindex = 0; yindex <= xindex; yindex++)
- DoPlot(xindex, yindex);
- }
-
- for (yindex = 0; yindex <= gMaxX; yindex++) { /* Plot along Y axis */
- gAtLineStart = true;
- for (xindex = yindex; xindex <= gMaxX; xindex++)
- DoPlot(xindex, yindex);
- }
-
- for (xindex = 0; xindex <= gMaxX; xindex++) { /* Plot along the diagonal */
- gAtLineStart = true;
- for (yindex = 0; yindex <= gMaxX - xindex; yindex++)
- DoPlot(xindex + yindex, yindex);
- }
- }
-
-
- /*
- DoPlot
-
- This function plots a single line or point from the current X and
- Y value to the new X and Y value specified.
- */
- void DoPlot(short xindex, short yindex)
- {
- short xcoord, ycoord, zcoord;
-
- gPixelBase = (char*)GetPixBaseAddr(gOffscreenPixMap);
-
- zcoord = GetZValue(xindex, yindex);
- ycoord = ScaleValue(yindex, 10000, gMaxX);
- xcoord = ScaleValue(xindex, 10000, gMaxX) - ycoord/2;
- if (gContourType == kStyleWater)
- SeaLevel(&xcoord, &ycoord, &zcoord);
- PlotTo(xcoord, ycoord, zcoord);
- }
-
-
- /*
- SeaLevel
-
- This function is used to plot lines when using the “foothills
- with water” mode. It determines whether a line goes under
- water at any point. If it does, it simply draws a point
- to represent the water.
- */
- void SeaLevel(short *newX, short *newY, short *newZ)
- {
- static short oldX, oldY, oldZ; /* The starting point for the next call */
- short waterX, waterY, waterZ; /* Where the vector hits the waterline. */
- float scratch;
-
- if (gAtLineStart) { /* If at the beginning of the line */
- if ((oldZ = *newZ) < 0) { /* and if we’re underwater */
- SetLineColor(blueColor);
- gOnLand = false;
- *newZ = 0; /* Clip to the waterline */
- }
- else {
- SetLineColor(blackColor);
- gOnLand = true; /* Otherwise we’re on land from the start */
- }
- }
- else { /* Else we’re in the middle of a line and */
- if (oldZ > 0 && *newZ > 0) { /* Start & end points both above water.. */
- oldZ = *newZ;
- }
- else if (oldZ < 0 && *newZ < 0) { /* Start & end points both under water... */
- oldZ = *newZ;
- *newZ = 0; /* Clip at the waterline */
- }
- else { /* We’re now crossing the waterline, so calculate */
- /* the exact point where it dives under */
- scratch = (float) (*newZ) / (*newZ - oldZ); /* Proportion of the line that’s */
- waterX = (short) ((oldX - *newX) * scratch) + *newX; /* below the water */
- waterY = (short) ((oldY - *newY) * scratch) + *newY;
- waterZ = 0;
-
- PlotTo(waterX, waterY, waterZ); /* Draw to the waterline first */
-
- /* The plot from the waterline to the endpoint in the new color is done elsewhere */
- if (*newZ > 0) { /* Emerging from the water */
- SetLineColor(blackColor);
- gOnLand = true;
- oldZ = *newZ;
- }
- else { /* Diving into the water. */
- SetLineColor(blueColor);
- gOnLand = false;
- oldZ = *newZ;
- *newZ = 0;
- }
- }
- }
- oldX = *newX; /* Save the real endpoint of the vector */
- oldY = *newY; /* to use as the start of the next call */
- }
-
-
- /*
- ScaleValue
-
- Computes base * numer / denom with long intermediate
- */
- short ScaleValue(short base, short numer, short denom)
- {
- long temp;
-
- temp = (long) base * (long) numer;
- temp /= (long) denom;
- return (short) temp;
- }
-
-
- /*
- PlotTo
-
- This function converts a 3-D line to a 2-D line and plots it
- */
- void PlotTo(short x, short y, short z)
- {
- Rotate(&x, &y); /* Rotate 30 deg. towards Y in the XY plane */
- TiltDown(&x, &z); /* Tip 36 deg. down in the ZX plane */
- x /= 25; /* Scale 10K to 400. */
- y /= 25;
- z /= 25;
- DrawLine(y, z); /* Show the YZ planar projection. */
- }
-
-
- /*
- Rotate
-
- This function rotates a point in the XY plane a + 30 degress.
- */
- void Rotate(short *x, short *y)
- {
- Cordic(x, y, 5, 17);
- }
-
-
- /*
- TiltDown
-
- This function rotates a point in the XZ plane a + or - 36 degress.
- */
- void TiltDown(short *x, short *z)
- {
- Cordic(x, z, 5, (gContourType == kStyleMountains) ? 20 : -20);
- }
-
-
- /*
- DrawLine
-
- This function draws a line from the last endpoint
- */
- void DrawLine(short x, short y)
- {
- x += x/10 + 10; /* Compute x1.1 + tiny offset */
- if (gContourType == kStyleMountains)
- y = 220 - y; /* Move the baseline for mountains */
- else
- y = 80 - y;
-
- if (gAtLineStart || !gOnLand) /* Only a point then */
- ScaledMoveTo(x, y);
-
- ScaledLineTo(x, y);
- gAtLineStart = false;
- }
-
-
- /*
- Cordic
-
- This function spins the XY vector ‘count’ steps to the left
- using a CORDIC algorithm with a shift factor of ‘scale.’
- Rotates atan(1/(2^scale)) degrees/step (e.g. scale of 5 is
- 1.79 deg/step; 4 = 3.57 d/s...)
-
- *x and *y should be large for accuracy.
- */
- void Cordic(short *x, short *y, short scale, short count)
- {
- short tempX, tempY;
-
- tempX = *x;
- tempY = *y;
- if (count > 0) /* Positive count is CCW (left) */
- for (; count; count--) {
- tempX -= (tempY >> scale);
- tempY += (tempX >> scale);
- }
- else /* Negative is CW (right) */
- for (; count; count++) {
- tempX += (tempY >> scale);
- tempY -= (tempX >> scale);
- }
- *x = tempX;
- *y = tempY;
- }
-
-
- /*
- ScaledMoveTo
-
- This function performs a scaled MoveTo by scaling the given
- point from MacPlus screen coordinates to the size of the
- current main window.
- */
- void ScaledMoveTo(short x, short y)
- {
- short scaledX, scaledY;
-
- scaledX = (long)x * (long)kNewScreenX / kOriginalScreenX;
- scaledY = (long)y * (long)kNewScreenY / kOriginalScreenY;
-
- MoveToOffscreen(scaledX, scaledY);
- }
-
-
- /*
- ScaledLineTo
-
- This function performs a scaled LineTo by scaling the given
- point from MacPlus screen coordinates to the size of the
- current main window.
- */
- void ScaledLineTo(short x, short y)
- {
- short scaledX, scaledY;
-
- scaledX = (long)x * (long)kNewScreenX / kOriginalScreenX;
- scaledY = (long)y * (long)kNewScreenY / kOriginalScreenY;
-
- LineToOffscreen(scaledX, scaledY);
- }
-
-
- /*
- SetLineColor
- */
- void SetLineColor(short colorCode)
- {
- RGBColor curColor;
-
- ForeColor(colorCode);
- GetForeColor(&curColor);
-
- gPixelColor = Color2Index(&curColor);
- }
-
-
- /*
- MoveToOffscreen
- */
- void MoveToOffscreen(short x, short y)
- {
- gCurrentX = x;
- gCurrentY = y;
- }
-
-
- /*
- LineToOffscreen
- */
- void LineToOffscreen(short x, short y)
- {
- if (abs(y - gCurrentY) > abs(x - gCurrentX)) {
- if (y < gCurrentY)
- LineYOffscreen(x, y, gCurrentX, gCurrentY);
- else
- LineYOffscreen(gCurrentX, gCurrentY, x, y);
- }
- else {
- if (x < gCurrentX)
- LineXOffscreen(x, y, gCurrentX, gCurrentY);
- else
- LineXOffscreen(gCurrentX, gCurrentY, x, y);
- }
-
- gCurrentX = x;
- gCurrentY = y;
- }
-
-
- /*
- LineXOffscreen
-
- Draws lines with a slope between -1 and 1
- */
- void LineXOffscreen(short startX, short startY, short endX, short endY)
- {
- short currentX;
- float slope, currentY, newY;
- float yRowIncrement;
- char* pixelAddress;
- long deltaY;
-
- if (endX == startX)
- slope = 0;
- else
- slope = (float)(endY - startY) / (float)(endX - startX);
-
- pixelAddress = gPixelBase + startX;
- yRowIncrement = (short)((*gOffscreenPixMap)->rowBytes & 0x7FFF);
- pixelAddress += (unsigned long)(startY * yRowIncrement);
- currentY = startY + 0.5;
-
- for (currentX = startX; currentX <= endX; currentX++) {
- *pixelAddress++ = gPixelColor;
- newY = currentY + slope;
- pixelAddress += (long)(yRowIncrement * ((long)(newY) - (long)(currentY)));
- currentY = newY;
- }
- }
-
-
- /*
- LineYOffscreen
-
- Draws lines with a slope <= -1 or >= 1
- */
- void LineYOffscreen(short startX, short startY, short endX, short endY)
- {
- short currentY;
- float slope, currentX, newX;
- unsigned long yRowIncrement;
- char* pixelAddress;
-
- if (endY == startY)
- slope = 0;
- else
- slope = (float)(endX - startX) / (float)(endY - startY);
-
- pixelAddress = gPixelBase + startX;
- yRowIncrement = (*gOffscreenPixMap)->rowBytes & 0x7FFF;
- pixelAddress += (unsigned long)(startY * yRowIncrement);
- currentX = startX + 0.5;
-
- for (currentY = startY; currentY <= endY; currentY++) {
- *pixelAddress = gPixelColor;
- newX = currentX + slope;
- pixelAddress += yRowIncrement + (long)(newX) - (long)(currentX);
- currentX = newX;
- }
- }
-
-
-
-
-
-
-
-